interface Service {
    getName(): string;
    execute(): void;
}

class ServiceA implements Service {
    public execute(): void {
        console.log("ServiceA executed");
    }
    public getName(): string {
        return "ServiceA";
    }
}

class ServiceB implements Service {
    public execute(): void {
        console.log("ServiceB executed");
    }
    public getName(): string {
        return "ServiceB";
    }
}

class InitialContext {
    public lookup(serviceName: string): Service | null {
        switch (serviceName.toUpperCase()) {
            case "SERVICEA":
                console.log('Looking up and creating a new instance of ServiceA');
                return new ServiceA();
            case "SERVICEB":
                console.log('Looking up and creating a new instance of ServiceB');
                return new ServiceB();
            default:
                return null;
        }
    }
}

class MyCache {
    private services: Service[];
    public constructor() {
        this.services = [];
    }
    public getService(serviceName: string): Service | null {
        for (const service of this.services) {
            if (service.getName().toUpperCase() === serviceName.toUpperCase()) {
                console.log(`Returning cached instance of ${serviceName}`);
                return service;
            }
        }
        return null;
    }
    public addService(newService: Service): void {
        for (const service of this.services) {
            if (service.getName().toUpperCase() === newService.getName().toUpperCase()) {
                return;
            }
        }
        this.services.push(newService);
    }
}

class ServiceLocator {
    private static cache: MyCache;
    static {
        this.cache = new MyCache();
    }
    public static getService(serviceName: string): Service | null {
        const service: Service | null = this.cache.getService(serviceName);
        if (service !== null) {
            return service;
        }
        const context: InitialContext = new InitialContext();
        const newService: Service = context.lookup(serviceName) as Service;
        this.cache.addService(newService);
        return newService;
    }
}

function main() {
    let service: Service = ServiceLocator.getService("ServiceA") as Service;
    service.execute();
    service = ServiceLocator.getService("ServiceB") as Service;
    service.execute();
    service = ServiceLocator.getService("ServiceA") as Service;
    service.execute();
    service = ServiceLocator.getService("ServiceB") as Service;
    service.execute();
}

main();
